import pdb

class svmr:

    def __init__(self,X,D,C=1.0,e=0.1):
        self.X = X
        self.N = len(X)
        self.Xdots = [[] for i in range(self.N)]
        for i in range(self.N):  
            for j in range(self.N):
                self.Xdots[i].append(self.dot(X[i],X[j]))
        self.D = D
        self.C = C
        self.e = e
        self.a1 = [0.0 for i in range(self.N)]
        self.a2 = [0.0 for i in range(self.N)]
        print self.Xdots
        
    def objfunc(self):
        res = 0.0
        res += sum([self.D[i]*(self.a1[i]-self.a2[i]) for i in range(self.N)])
        res -= sum([self.e*(self.a1[i]+self.a2[i]) for i in range(self.N)])
        res -= sum([(self.a1[i]-self.a2[i])*(self.a1[j]-self.a2[j])*self.Xdots[i][j] for i in range(self.N) for j in range(self.N)])
        return res

    def objfuncda(self,i,prime=False):
        if not prime:
            res = self.D[i]*(1-self.a2[i]) - self.e*(1-self.a2[i]) 
            res -= sum([(self.a1[j]-self.a2[j])*self.Xdots[i][j] for j in range(self.N)])
        else:
            res = self.D[i]*(self.a1[i]-1) - self.e*(self.a1[i]-1) 
            res -= sum([-1.0*(self.a1[j]-self.a2[j])*self.Xdots[i][j] for j in range(self.N)])
        return res

    def weights(self):
        return self.sumv([self.mults(self.X[i],(self.a1[i]-self.a2[i])) for i in range(self.N)])
    

    def dot(self,X1,X2):
        return reduce(lambda x,y : x+y,[X1[i]*X2[i] for i in range(len(X1))])

    def mults(self,X,s):
        return [x*s for x in X]
    
    def addv(self,X,Y):
        return [X[i]+Y[i] for i in range(len(X))]

    def sumv(self,X):
        return reduce(lambda a,b:self.addv(a,b),X)

    def graddesc(self,rate,limit):
        self.scores = [self.objfunc()]
        self.da1 = []
        self.da2 = []
        
        print 'score:',self.scores[0]
        for step in range(limit):
            newa1 = self.a1[:]
            newa2 = self.a2[:]
            for n in range(self.N):

                da = rate*self.objfuncda(n)
                da = min([max([da,-self.a1[n]]),self.C-self.a1[n]])
                self.da1.append(da)
                #print 'da1:',da
                newa1[n] = self.a1[n]+da

                da = rate*self.objfuncda(n,True)
                da = min([max([da,-self.a2[n]]),self.C-self.a2[n]])
                self.da2.append(da)
                #print 'da2:',da
                newa2[n] = self.a2[n]+da

                if newa1[n] < 0: newa1[n] = 0
                if newa2[n] < 0: newa2[n] = 0
            
            self.a1 = newa1[:]
            self.a2 = newa2[:]

            self.scores.append(self.objfunc())
            #if step > 100 and self.scores[step]==self.scores[step+1] :break

        print 'scores:',self.scores[len(self.scores)-100:]
        print 'das 1:',self.scores[len(self.scores)-100:]
        print 'das 2:',self.scores[len(self.scores)-100:]
        
        print 'alpha:',self.a1
        print 'alpha prime:',self.a2
        print 'steps:',step
        
if __name__=='__main__':
    #test function, f(X) = 2(X[1]+X[2])
    X = [[2.,2.,1.],[3.,3.,1.],[4.,4.,1.],[5.,5.,1.]]
    D = [9.,13.,17.,21.]
    s = svmr(X,D,C=.2,e=0.)
    s.graddesc(0.01,10000)
    w = s.weights()
    print 'w:',w
    print 't:',D
    print [s.dot(x,w) for x in X]
